#include <ptgui/ptgui.h>
#include <ptgui/memory.h>

#include <malloc.h>
#include <memory.h>

/************************************************************************/
/* RTGUI Memory Management  											*/
/************************************************************************/
#ifdef PTGUI_FEATHER_MEM_TRACE
struct ptgui_mem_info
{
	rt_uint32_t	allocated_size;
	rt_uint32_t	max_allocated;
};
struct ptgui_mem_info mem_info;

#define MEMTRACE_MAX		4096
#define MEMTRACE_HASH_SIZE	256

struct ptgui_memtrace_item
{
	void						*mb_ptr;		/* memory block pointer */
	rt_uint32_t					mb_len;		/* memory block length */

	struct ptgui_memtrace_item	*next;
};
struct ptgui_memtrace_item trace_list[MEMTRACE_MAX];
struct ptgui_memtrace_item *item_hash[MEMTRACE_HASH_SIZE];
struct ptgui_memtrace_item *item_free;

rt_bool_t ptgui_memtrace_inited = 0;
void ptgui_memtrace_init()
{
	struct ptgui_memtrace_item *item;
	rt_uint32_t index;

	ptgui_memset(trace_list, 0, sizeof(trace_list));
	ptgui_memset(item_hash, 0, sizeof(item_hash));

	item_free = &trace_list[0];
	item = &trace_list[0];

	for (index = 1; index < MEMTRACE_HASH_SIZE; index ++) {
		item->next = &trace_list[index];
		item = item->next;
	}

	item->next = RT_NULL;
}

void ptgui_malloc_hook(void *ptr, rt_uint32_t len)
{
	rt_uint32_t index;
	struct ptgui_memtrace_item *item;

	if (item_free == RT_NULL) {
		return;
	}

	mem_info.allocated_size += len;
	if (mem_info.max_allocated < mem_info.allocated_size) {
		mem_info.max_allocated = mem_info.allocated_size;
	}

	/* lock context */
	item = item_free;
	item_free = item->next;

	item->mb_ptr = ptr;
	item->mb_len = len;
	item->next = RT_NULL;

	/* get hash item index */
	index = ((rt_uint32_t)ptr) % MEMTRACE_HASH_SIZE;
	if (item_hash[index] != RT_NULL) {
		/* add to list */
		item->next = item_hash[index];
		item_hash[index] = item;
	}
	else {
		/* set list header */
		item_hash[index] = item;
	}
	/* unlock context */
}

void ptgui_free_hook(void *ptr)
{
	rt_uint32_t index;
	struct ptgui_memtrace_item *item;

	/* get hash item index */
	index = ((rt_uint32_t)ptr) % MEMTRACE_HASH_SIZE;
	if (item_hash[index] != RT_NULL) {
		item = item_hash[index];
		if (item->mb_ptr == ptr) {
			/* delete item from list */
			item_hash[index] = item->next;
		}
		else {
			/* find ptr in list */
			while (item->next != RT_NULL && item->next->mb_ptr != ptr)
				item = item->next;

			/* delete item from list */
			if (item->next != RT_NULL) {
				struct ptgui_memtrace_item *i;

				i = item->next;
				item->next = item->next->next;

				item = i;
			}
			else {
				/* not found */
				return;
			}
		}

		/* reduce allocated size */
		mem_info.allocated_size -= item->mb_len;

		/* clear item */
		ptgui_memset(item, 0, sizeof(struct ptgui_memtrace_item));

		/* add item to the free list */
		item->next = item_free;
		item_free = item;
	}
}
#endif

void * ptgui_malloc(rt_size_t size)
{
	void *ptr;

	ptr = malloc(size);

#ifdef PTGUI_FEATHER_MEM_TRACE
	if (ptgui_memtrace_inited == 0) {
		ptgui_memtrace_init();
		ptgui_memtrace_inited = 1;
	}

	if (ptr != RT_NULL) {
		ptgui_malloc_hook(ptr, size);
	}
#endif
	return ptr;
}

void * ptgui_realloc(void *ptr, rt_size_t size)
{
	void *newptr;

#ifdef PTGUI_FEATHER_MEM_TRACE
	if (ptgui_memtrace_inited == 0) {
		ptgui_memtrace_init();
		ptgui_memtrace_inited = 1;
	}

	if (ptr != RT_NULL) {
		ptgui_free_hook(ptr);
	}
#endif

	newptr = realloc(ptr, size);
#ifdef PTGUI_FEATHER_MEM_TRACE
	if (ptgui_memtrace_inited == 0) {
		ptgui_memtrace_init();
		ptgui_memtrace_inited = 1;
	}

	if (newptr != RT_NULL) {
		ptgui_malloc_hook(newptr, size);
	}
#endif

	return newptr;
}

void ptgui_free(void *ptr)
{
#ifdef PTGUI_FEATHER_MEM_TRACE
	if (ptr != RT_NULL) {
		ptgui_free_hook(ptr);
	}
#endif

	free(ptr);
}
